﻿/*******************************************************************************
Copyright (c) 2011-2013, PKStudio
Copyright (c) 2011,      Zynga Inc.
Copyright (c) 2008-2010, Ricardo Quesada

http://pkstudio.org

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*******************************************************************************/

using System;
using System.Collections.Generic;
using System.Drawing;

namespace PKEngine
{
	public interface IPKNotification
	{
		void Notification(object notificationInfo);
	}

	public class PKSpriteFrame
	{
		#region Pubilc Properties
		/// <summary>
		/// Rect of the frame.
		/// </summary>
		public Rectangle Rect { get; private set; }

		/// <summary>
		/// Surface of the frame.
		/// </summary>
		public PKSurface Surface { get; private set; }
		#endregion

		#region Initialization
		public PKSpriteFrame(PKSurface surface, Rectangle rect)
		{
			this.Surface = surface;
			this.Rect = rect;
		}
		#endregion
	}

	public class PKAnimationFrame
	{
		#region Pubilc Properties
		/// <summary>
		/// SpriteFrame of the AnimationFrame.
		/// </summary>
		public PKSpriteFrame SpriteFrame { get; private set; }

		/// <summary>
		/// How many units of time the frame takes.
		/// </summary>
		public float DelayUnits { get; set; }

		/// <summary>
		/// If the notification is not null, a notification will be broadcasted when the frame is displayed.
		/// </summary>
		public object NotificationInfo { get; set; }
		#endregion

		#region Initialization
		public PKAnimationFrame(PKSpriteFrame spriteFrame, float delayUnits)
			: this(spriteFrame, delayUnits, null)
		{
		}

		public PKAnimationFrame(PKSpriteFrame spriteFrame, float delayUnits, object notificationInfo)
		{
			this.SpriteFrame = spriteFrame;
			this.DelayUnits = delayUnits;
			this.NotificationInfo = notificationInfo;
		}
		#endregion
	}

	/// <summary>
	/// A PKAnimation object is used to perform animations on the CCSprite objects.
	/// You can animate a PKAnimation object by using the PKAnimate action.
	/// </summary>
	public class PKAnimation
	{
		#region Pubilc Properties
		/// <summary>
		/// Total Delay units of PKAnimation.
		/// </summary>
		public float TotalDelayUnits { get; private set; }

		/// <summary>
		/// Delay per unit of PKAnimation.
		/// </summary>
		public float DelayPerUnit { get; set; }

		/// <summary>
		/// Duration in seconds of the whole animation.
		/// It is the result of TotalDelayUnits * DelayPerUnit.
		/// </summary>
		public float Duration
		{
			get
			{
				return this.TotalDelayUnits * this.DelayPerUnit;
			}
		}

		/// <summary>
		/// Frames of PKAnimation.
		/// </summary>
		public List<PKAnimationFrame> Frames { get; private set; }

		/// <summary>
		/// Whether or not it shall restore the original frame when the animation finishes.
		/// </summary>
		public bool RestoreOriginalFrame { get; set; }

		/// <summary>
		/// How many times the animation is going to loop.
		/// </summary>
		public int Loops { get; set; }
		#endregion

		#region Initialization
		/// <summary>
		/// Creates an animation.
		/// </summary>
		public PKAnimation()
			: this(null, 0)
		{
		}

		/// <summary>
		/// Creates an animation with frames.
		/// </summary>
		/// <param name="frames">Frames</param>
		public PKAnimation(PKSpriteFrame[] frames)
			: this(frames, 0)
		{
		}

		/// <summary>
		/// Creates an animation with frames and a delay between frames in seconds.
		/// </summary>
		/// <param name="frames">Frames</param>
		/// <param name="delay">Delay</param>
		public PKAnimation(PKSpriteFrame[] frames, float delay)
		{
			this.Loops = 1;
			this.DelayPerUnit = delay;
			if (frames != null && frames.Length > 0)
			{
				this.Frames = new List<PKAnimationFrame>(frames.Length);
				foreach (PKSpriteFrame frame in frames)
				{
					this.Frames.Add(new PKAnimationFrame(frame, 1));
					this.TotalDelayUnits++;
				}
			}
			else
			{
				this.Frames = new List<PKAnimationFrame>();
			}
		}

		/// <summary>
		/// Creates an animation with frames and the delay per units in seconds.
		/// </summary>
		/// <param name="frames">Frames</param>
		/// <param name="delayPerUnit">Delay per unit</param>
		/// <param name="loops">Loops</param>
		public PKAnimation(PKAnimationFrame[] frames, float delayPerUnit, int loops)
		{
			this.Loops = loops;
			this.DelayPerUnit = delayPerUnit;
			if (frames != null && frames.Length > 0)
			{
				this.Frames = new List<PKAnimationFrame>(frames);
				foreach (PKAnimationFrame frame in frames)
				{
					this.TotalDelayUnits += frame.DelayUnits;
				}
			}
			else
			{
				this.Frames = new List<PKAnimationFrame>();
			}
		}
		#endregion

		#region Public Methods
		/// <summary>
		/// Add a new frame.
		/// </summary>
		/// <param name="frame">New frame</param>
		public void AddFrame(PKSpriteFrame frame)
		{
			this.Frames.Add(new PKAnimationFrame(frame, 1));
			this.TotalDelayUnits++;
		}

		/// <summary>
		/// Add a new frame with filename.
		/// </summary>
		/// <param name="fileName">File name of new frame</param>
		public void AddFrame(string fileName)
		{
			PKSurface surface = PKSurfaceCache.SharedSurfaceCache.AddImage(fileName);
			Rectangle rect = new Rectangle(Point.Empty, surface.ContentSize);
			this.AddFrame(new PKSpriteFrame(surface, rect));
		}

		/// <summary>
		/// Add a new frame with surface.
		/// </summary>
		/// <param name="surface">Surface of new frame</param>
		/// <param name="rect">Rectangle of new frame</param>
		public void AddFrame(PKSurface surface, Rectangle rect)
		{
			this.AddFrame(new PKSpriteFrame(surface, rect));
		}
		#endregion
	}
}
